{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "56cd0442",
   "metadata": {},
   "source": [
    "## Map-reduce\n",
    "\n",
    "### 目标\n",
    "现在，我们将介绍 map Reduce。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "bf82889c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "OPENAI_API_KEY: ········\n",
      "OPENAI_BASE_URL: ········\n"
     ]
    }
   ],
   "source": [
    "import os, getpass\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "_set_env(\"OPENAI_API_KEY\")\n",
    "_set_env(\"OPENAI_BASE_URL\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "0b8c5e59",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "LANGCHAIN_API_KEY: ········\n"
     ]
    }
   ],
   "source": [
    "_set_env(\"LANGCHAIN_API_KEY\")\n",
    "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
    "os.environ[\"LANGCHAIN_PROJECT\"] = \"sub_graph\"\n",
    "os.environ['USER_AGENT'] = 'myagent'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5eeea7dc",
   "metadata": {},
   "source": [
    "## 问题\n",
    "Map-reduce操作对于高效任务分解和并行处理至关重要。<br/>\n",
    "它有两个阶段：<br/>\n",
    "（1）Map -将任务分解为较小的子任务，并行处理每个子任务。<br/>\n",
    "（2）Reduce - 汇总所有已完成的并行子任务的结果。<br/>\n",
    "\n",
    "让我们设计一个可以做两件事的系统：<br/>\n",
    "（1）Map -创建一组关于某个主题的笑话。<br/>\n",
    "（2）Reduce -从列表中挑选最好的笑话。<br/>\n",
    "\n",
    "我们将使用LLM来完成笑话生成和选择。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "80531af2",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "# 我们将使用的prompt\n",
    "subjects_prompt = \"\"\"Generate a list of 3 sub-topics that are all related to this overall topic: {topic}.\"\"\"\n",
    "joke_prompt = \"\"\"Generate a joke about {subject}\"\"\"\n",
    "best_joke_prompt = \"\"\"Below are a bunch of jokes about {topic}. Select the best one! Return the ID of the best one, starting 0 as the ID for the first joke. Jokes: \\n\\n {jokes}\"\"\"\n",
    "\n",
    "# LLM\n",
    "model = ChatOpenAI(model=\"gpt-4o\", temperature=0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "39a5d46a",
   "metadata": {},
   "outputs": [],
   "source": [
    "import operator\n",
    "from typing import Annotated\n",
    "from typing_extensions import TypedDict\n",
    "from pydantic import BaseModel\n",
    "\n",
    "class Subjects(BaseModel):\n",
    "    subjects: list[str]\n",
    "\n",
    "class BestJoke(BaseModel):\n",
    "    id: int\n",
    "    \n",
    "class OverallState(TypedDict):\n",
    "    topic: str\n",
    "    subjects: list\n",
    "    jokes: Annotated[list, operator.add] # 笑话键\n",
    "    best_selected_joke: str"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "defeec2b",
   "metadata": {},
   "outputs": [],
   "source": [
    "def generate_topics(state: OverallState):\n",
    "    prompt = subjects_prompt.format(topic=state[\"topic\"])\n",
    "    response = model.with_structured_output(Subjects).invoke(prompt)\n",
    "    return {\"subjects\": response.subjects}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "8dc9078f",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph.constants import Send\n",
    "def continue_to_jokes(state: OverallState):\n",
    "    return [Send(\"generate_joke\", {\"subject\": s}) for s in state[\"subjects\"]]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "1275ce97",
   "metadata": {},
   "outputs": [],
   "source": [
    "class JokeState(TypedDict):\n",
    "    subject: str\n",
    "\n",
    "class Joke(BaseModel):\n",
    "    joke: str\n",
    "\n",
    "def generate_joke(state: JokeState):\n",
    "    prompt = joke_prompt.format(subject=state[\"subject\"])\n",
    "    response = model.with_structured_output(Joke).invoke(prompt)\n",
    "    return {\"jokes\": [response.joke]}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "768411bc",
   "metadata": {},
   "source": [
    "魔法就在这里：我们使用Send 为每个主题创建一个笑话。<br/>\n",
    "\n",
    "这非常有用！它可以自动并行生成任意数量的主题的笑话。\n",
    "\n",
    "- generate_joke: 图中节点的名称\n",
    "- {\"subject\": s} : 要发送的状态\n",
    "\n",
    "Send允许您将任何您想要的状态传递给generate_joke! 它不必与OverallState保持一致。\n",
    "\n",
    "在这种情况下，generate_joke正在使用它自己的内部状态，我们可以通过Send填充它。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "bcebdfce",
   "metadata": {},
   "outputs": [],
   "source": [
    "def best_joke(state: OverallState):\n",
    "    jokes = \"\\n\\n\".join(state[\"jokes\"])\n",
    "    prompt = best_joke_prompt.format(topic=state[\"topic\"], jokes=jokes)\n",
    "    response = model.with_structured_output(BestJoke).invoke(prompt)\n",
    "    return {\"best_selected_joke\": state[\"jokes\"][response.id]}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "70a93812",
   "metadata": {},
   "source": [
    "## 组合起来"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "cd2d1cd3",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from IPython.display import Image\n",
    "from langgraph.graph import END, StateGraph, START\n",
    "\n",
    "# 构建图表：在这里我们将所有内容放在一起来构建我们的图表\n",
    "graph = StateGraph(OverallState)\n",
    "graph.add_node(\"generate_topics\", generate_topics)\n",
    "graph.add_node(\"generate_joke\", generate_joke)\n",
    "graph.add_node(\"best_joke\", best_joke)\n",
    "graph.add_edge(START, \"generate_topics\")\n",
    "graph.add_conditional_edges(\"generate_topics\", continue_to_jokes, [\"generate_joke\"])\n",
    "graph.add_edge(\"generate_joke\", \"best_joke\")\n",
    "graph.add_edge(\"best_joke\", END)\n",
    "\n",
    "# 编译图表\n",
    "app = graph.compile()\n",
    "Image(app.get_graph().draw_mermaid_png())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f962fa2e",
   "metadata": {},
   "source": [
    "## 状态\n",
    "并行笑话生成 <br/>\n",
    "\n",
    " - 获取用户输入主题。\n",
    " - 从中生成笑话主题列表。\n",
    " - 将每个笑话主题发送到我们上面的笑话生成节点。\n",
    " \n",
    "我们的状态有一个“笑话”键，它将从并行化笑话生成中积累笑话\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "4c2ada6e",
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'generate_topics': {'subjects': ['玩具的历史与演变', '玩具的教育功能', '玩具的安全与环保']}}\n",
      "{'generate_joke': {'jokes': ['为什么玩具的历史学家总是很开心？因为他们总是玩得很认真！']}}\n",
      "{'generate_joke': {'jokes': ['为什么玩具喜欢环保？因为它们想成为“绿色”玩具，而不是“灰色”垃圾！']}}\n",
      "{'generate_joke': {'jokes': ['为什么玩具是最好的老师？因为它们总是让孩子们在玩中学！']}}\n",
      "{'best_joke': {'best_selected_joke': '为什么玩具是最好的老师？因为它们总是让孩子们在玩中学！'}}\n"
     ]
    }
   ],
   "source": [
    "\n",
    "# 调用图表：这里我们调用它来生成一个笑话列表\n",
    "for s in app.stream({\"topic\": \"玩具\"}):\n",
    "    print(s)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8474b518",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.10",
   "language": "python",
   "name": "python310"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.16"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
